﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.IO;
using System.Xml.Serialization;
using System.Globalization;
using System.Collections.ObjectModel;

namespace GIALib
{
    /// <summary>
    /// Representation for a single serializable Person.
    /// INotifyPropertyChanged allows properties of the Person class to
    /// participate as source in data bindings.
    /// </summary>
    [Serializable]  //声明这个类可以序列化
    public class Person : INotifyPropertyChanged, IEquatable<Person>
    {
        #region Fields and Constants

        /// <summary>
        /// The constants specific to this class
        /// </summary>
        private static class Const
        {
            public const string DefaultName = "Unknown";
        }

        private string id;          //id
        private string name;        //姓名
        private string nickName;    //昵称
        private Gender gender;      //性别
        private Position position;  //职位
        private DateTime? birthDate;//出生日期
        private string telephone;   //电话
        private string address;     //住址
        private PhotoCollection photos;//照片
        private Resume resume;      //简历
        private RelationshipCollection relationships;   //人员关系

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the unique identifier for each person.
        /// </summary>
        [XmlAttribute]  //此属性可作为xml元素的属性
        public string Id
        {
            get { return id; }
            set
            {
                if (id != value)
                {
                    id = value;
                    OnPropertyChanged("Id");
                }
            }
        }

        /// <summary>
        /// Gets or sets the name of the person.
        /// </summary>
        public string Name
        {
            get 
            {
                if (string.IsNullOrEmpty(name))
                {
                    return "";
                }
                else
                {
                    return name;
                }
            }
            set
            {
                if (name != value)
                {
                    name = value;
                    OnPropertyChanged("Name");
                }
            }
        }

        /// <summary>
        /// Gets or sets the person's familiar or shortened name
        /// </summary>
        public string NickName
        {
            get { return nickName; }
            set
            {
                if (nickName != value)
                {
                    nickName = value;
                    OnPropertyChanged("NickName");
                }
            }
        }

        /// <summary>
        /// Gets or sets the person's gender
        /// </summary>
        public Gender Gender
        {
            get { return gender; }
            set
            {
                if (gender != value)
                {
                    gender = value;
                    OnPropertyChanged("Gender");
                }
            }
        }

        /// <summary>
        /// Gets or sets the person's position
        /// </summary>
        public Position Position
        {
            get {return position;}
            set
            {
                if(position != value)
                {
                    position = value;
                    OnPropertyChanged("Position");
                }
            }
        }

        /// <summary>
        /// Gets or sets the person's birth date.  This property can be null.
        /// </summary>
        public DateTime? BirthDate
        {
            get { return birthDate; }
            set
            {
                if (birthDate == null || birthDate != value)
                {
                    birthDate = value;
                    OnPropertyChanged("BirthDate");     //出生的年月日
                    OnPropertyChanged("Age");           //年龄
                    OnPropertyChanged("AgeGroup");      //年龄段
                    OnPropertyChanged("YearOfBirth");   //出生的年份
                    OnPropertyChanged("BirthDay");      //生日，月日的形式
                }
            }
        }

        public string Telephone
        {
            get { return telephone; }
            set
            {
                if (telephone != value)
                {
                    telephone = value;
                    OnPropertyChanged("Telephone");
                }
            }
        }

        /// <summary>
        /// Gets or sets the person's place of birth
        /// </summary>
        public string Address
        {
            get { return address; }
            set
            {
                if (address != value)
                {
                    address = value;
                    OnPropertyChanged("Address");
                }
            }
        }

        /// <summary>
        /// Gets the age of the person
        /// </summary>
        public int? Age
        {
            get
            {
                if (this.BirthDate == null)
                {
                    return null;
                }

                // Determine the age of the person based on just the year.
                DateTime startDate = this.BirthDate.Value;
                DateTime endDate = DateTime.Now;
                int age = endDate.Year - startDate.Year;

                // Compensate for the month and day of month (if they have not had a birthday this year).
                if (endDate.Month < startDate.Month ||
                    (endDate.Month == startDate.Month && endDate.Day < startDate.Day))
                    age--;

                return Math.Max(0, age);
            }
        }

        /// <summary>
        /// The agegroup of the person.
        /// </summary>
        [XmlIgnore] //在xml中忽略此对象属性
        public AgeGroup AgeGroup
        {
            get
            {
                AgeGroup ageGroup = AgeGroup.Unknown;

                if (this.Age.HasValue)
                {
                    // The AgeGroup enumeration is defined later in this file. It is up to the Person
                    // class to define the ages that fall into the particular age groups
                    if (this.Age >= 0 && this.Age < 16)
                        ageGroup = AgeGroup.Teenage;
                    else if (this.Age >= 18 && this.Age < 40)
                        ageGroup = AgeGroup.Youth;
                    else if (this.Age >= 40 && this.Age < 65)
                        ageGroup = AgeGroup.MiddleAge;
                    else
                        ageGroup = AgeGroup.OldAge;
                }
                return ageGroup;
            }
        }

        /// <summary>
        /// The year the person was born
        /// </summary>
        public string YearOfBirth
        {
            get
            {
                if (birthDate.HasValue)
                    return birthDate.Value.Year.ToString(CultureInfo.CurrentCulture);
                else
                    return "-";
            }
        }

        /// <summary>
        /// Gets the month and day the person was born in. This property can be null.
        /// </summary>
        [XmlIgnore]
        public string BirthDay
        {
            get
            {
                if (this.BirthDate == null)
                    return null;
                else
                {
                    return birthDate.Value.ToString(
                        DateTimeFormatInfo.CurrentInfo.MonthDayPattern,
                        CultureInfo.CurrentCulture);
                }
            }
        }

        /// <summary>
        /// Gets or sets the photos associated with the person
        /// </summary>
        public PhotoCollection Photos
        {
            get { return photos; }
        }

        /// <summary>
        /// Gets or sets the person's resume file.
        /// </summary>
        public Resume Resume
        {
            get { return resume; }
            set
            {
                if (resume != value)
                {
                    resume = value;
                    OnPropertyChanged("Resume");
                }
            }
        }

        /// <summary>
        /// Gets or sets the person's graphical identity(头像)
        /// </summary>
        [XmlIgnore, System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "value")]
        public string Avatar
        {
            get
            {
                string avatar = "";

                if (photos != null && photos.Count > 0)
                {
                    foreach (Photo photo in photos)
                    {
                        if (photo.IsAvatar)
                            return photo.FullyQualifiedPath;
                    }
                }

                return avatar;
            }
            set
            {
                // This setter is used for change notification.
                OnPropertyChanged("Avatar");
                OnPropertyChanged("HasAvatar");
            }
        }

        /// <summary>
        /// Claculated property that returns whether the person has an avatar photo.
        /// </summary>
        [XmlIgnore]
        public bool HasAvatar
        {
            get
            {
                if (photos != null && photos.Count > 0)
                {
                    foreach (Photo photo in photos)
                    {
                        if (photo.IsAvatar)
                            return true;
                    }
                }

                return false;
            }
        }

        /// <summary>
        /// Determines whether a person is deletable.
        /// </summary>
        [XmlIgnore]
        public bool IsDeletable
        {
            get
            {
                //TODO:need modification
                return true;
            }
        }

        /// <summary>
        /// Get the collections of relationship connection for the person
        /// </summary>
        public RelationshipCollection Relationships
        {
            get { return relationships; }
        }

        /// <summary>
        /// Get the person's juniors
        /// </summary>
        [XmlIgnore]
        public Collection<Person> Juniors
        {
            get
            {
                Collection<Person> juniors = new Collection<Person>();
                foreach (Relationship rel in relationships)
                {
                    if (rel.RelationshipType == RelationshipType.Junior)
                        juniors.Add(rel.RelationTo);
                }
                return juniors;
            }
        }

        /// <summary>
        /// Get the person's seniors
        /// </summary>
        [XmlIgnore]
        public Collection<Person> Seniors
        {
            get
            {
                Collection<Person> seniors = new Collection<Person>();
                foreach (Relationship rel in relationships)
                {
                    if (rel.RelationshipType == RelationshipType.Senior)
                        seniors.Add(rel.RelationTo);
                }
                return seniors;
            }
        }

        /// <summary>
        /// Get the person's main senior
        /// </summary>
        [XmlIgnore]
        public Person Senior
        { 
            get 
            {
                if (this.Seniors.Count > 0)
                {
                    return this.Seniors[0];
                }
                else
                {
                    return null;
                }
            }
            
        }

        /// <summary>
        /// Get the person's workmates
        /// </summary>
        [XmlIgnore]
        public Collection<Person> Workmates
        {
            get
            {
                Collection<Person> workmates = new Collection<Person>();
                foreach (Relationship rel in relationships)
                {
                    if (rel.RelationshipType == RelationshipType.Workmate)
                        workmates.Add(rel.RelationTo);
                }
                return workmates;
            }
        }

        /// <summary>
        /// Get the possible seniors.
        /// </summary>
        [XmlIgnore]
        public SeniorListCollection PossibleSeniorList
        {
            get
            {
                SeniorListCollection slc = new SeniorListCollection();
                foreach (Person senior in Seniors)
                {
                    SeniorList sl = new SeniorList(senior);
                    if (!slc.Contains(sl))
                        slc.Add(sl);
                }
                return slc;
            }
        }

        /// <summary>
        /// Returns whether the person has senior.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "value")]
        public bool HasSeniors
        {
            get
            {
                return (Seniors.Count > 0);
            }
            set
            { 
                //This setter is used for change notification
                OnPropertyChanged("HasSeniors");
            }
        }

        /// <summary>
        /// Returns whether the person has junior.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "value")]
        public bool HasJuniors
        {
            get
            {
                return (Juniors.Count > 0);
            }
            set
            {
                //This setter is used for change notification
                OnPropertyChanged("HasJuniors");
            }
        }


        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new instance of a person object.
        /// Each new instance will be given a unique identifier.
        /// This parameterless constructor is also required for serialization.
        /// </summary>
        public Person()
        {
            this.id = Guid.NewGuid().ToString();//构造全局唯一标识符
            this.name = Const.DefaultName;
            this.nickName = string.Empty;
            this.gender = Gender.Male;
            this.position = Position.Following;
            this.birthDate = DateTime.Now;
            this.telephone = string.Empty;
            this.address = string.Empty;
            this.photos = new PhotoCollection();
            this.relationships = new RelationshipCollection();
            this.resume = new Resume();
        }

        /// <summary>
        /// Create a  new instance of the person with the name.
        /// </summary>
        /// <param name="name">person's name</param>
        public Person(string name)
            : this()
        {
            if (string.IsNullOrEmpty(name))
            {
                this.name = Const.DefaultName;
            }
            else
            {
                this.name = name;
            }
        }

        public Person(string name, Gender gender)
            : this(name)
        {
            this.gender = gender;
        }

#endregion




        #region INotifyPropertyChanged Members

        /// <summary>
        /// INotifyPropertyChanged requires a property called PropertyChanged.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;   //事件其实就是委托

        /// <summary>
        /// Fires the event for the property when it changes.
        /// </summary>
        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion

        #region IEquatable Members

        /// <summary>
        /// Determine equality between two person classes
        /// </summary>
        public bool Equals(Person other)
        {
            return (this.Id == other.Id);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Called to delete the person's photos
        /// </summary>
        public void DeletePhotos()
        {
            // Delete the person's photos
            foreach (Photo photo in this.photos)
            {
                photo.Delete();
            }
        }

        /// <summary>
        /// Called to delete the person's story
        /// </summary>
        public void DeleteResume()
        {
            if (this.resume != null)
            {
                this.resume.Delete();
                this.resume = null;
            }
        }

        public SeniorListCollection MakeSeniorList()
        {
            SeniorListCollection seniorList = new SeniorListCollection();

            foreach (Person workmate in Workmates)
            {
                SeniorList sl = new SeniorList(workmate);
                if (!seniorList.Contains(sl))
                    seniorList.Add(sl);
            }
            return seniorList;
        }

        #endregion

    }


    #region Enumerations

    /// <summary>
    /// Enumeration of the person's gender group
    /// </summary>
    public enum Gender
    {
        Male, Female
    }

    /// <summary>
    /// Enumeration of the person's age group
    /// 少年，青年，中年，老年
    /// </summary>
    public enum AgeGroup
    {
        Unknown, Teenage, Youth, MiddleAge, OldAge
    }

    /// <summary>
    /// Enumeration of the person's position group
    /// 主席，部长，经理，部员
    /// </summary>
    public enum Position
    { 
        Chairman, Minister, Manager, Following
    }

    #endregion

    #region Class SeniorList

    /// <summary>
    /// List a person's seniors.
    /// </summary>
    public class SeniorList : IEquatable<SeniorList>
    {
        private List<Person> seniorList = new List<Person>();

        #region Constructor of SeniorList

        public SeniorList() { }

        public SeniorList(Person p) 
        {
            seniorList.Add(p);
        }

        public SeniorList(List<Person> seniors) //CHOOSE:可以用深拷贝
        {
            foreach (Person senior in seniors)
            {
                seniorList.Add(senior);
            }
        }

        #endregion

        public string Name
        {
            get
            {
                string name = "";
                foreach (Person senior in seniorList)
                {
                    name += senior.Name + " ";
                }
                return name;
            }
        }

        #region IEquatable<SeniorList> Members

        /// <summary>
        /// Determine equality between two SeniorList classes.  Note: Bob and Sue == Sue and Bob
        /// </summary>
        public bool Equals(SeniorList other)    //CHOOSE:可以用其他更聪明的方法做比较
        {
            foreach (Person senior in seniorList)
            {
                if (!other.seniorList.Contains(senior))
                    return false;
            }
            foreach (Person senior in other.seniorList)
            {
                if (!seniorList.Contains(senior))
                    return false;
            }
            return true;
        }

        #endregion

    }


    /// <summary>
    /// Collection of SeniorList objects.
    /// </summary>
    public class SeniorListCollection : Collection<SeniorList> { }

    #endregion

    #region Class JuniorList

    /// <summary>
    /// List a person's juniors.
    /// </summary>
    public class JuniorList : IEquatable<JuniorList>
    {
        private List<Person> juniorList = new List<Person>();

        #region Constructor of JuniorList

        public JuniorList() { }

        public JuniorList(Person p)
        {
            juniorList.Add(p);
        }

        public JuniorList(List<Person> juniors) //CHOOSE:可以用深拷贝
        {
            foreach (Person junior in juniors)
            {
                juniorList.Add(junior);
            }
        }

        #endregion

        public string Name
        {
            get
            {
                string name = "";
                foreach (Person junior in juniorList)
                {
                    name += junior.Name + " ";
                }
                return name;
            }
        }

        #region IEquatable<SeniorList> Members

        /// <summary>
        /// Determine equality between two JuniorList classes.  Note: Bob and Sue == Sue and Bob
        /// </summary>
        public bool Equals(JuniorList other)    //CHOOSE:可以用其他更聪明的方法
        {
            foreach (Person junior in juniorList)
            {
                if (!other.juniorList.Contains(junior))
                    return false;
            }
            foreach (Person junior in other.juniorList)
            {
                if (!juniorList.Contains(junior))
                    return false;
            }
            return true;
        }

        #endregion
    }

    /// <summary>
    /// Collection of JuniorList objects.
    /// </summary>
    public class JuniorListCollection : Collection<JuniorList> { }

    #endregion

}


